﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using DimensionsHelper.Common.Contracts.DataCollector;
using DimensionsHelper.Common.Contracts.DataCollector.Options;
using DimensionsHelper.Common.Contracts.Excel;
using DimensionsHelper.Common.Contracts.Services;
using DimensionsHelper.Common.Utils;
using DimensionsHelper.Services.DataCollector.Options;
using DimensionsHelper.Services.Excel;

namespace DimensionsHelper.Services.DataCollector;

public sealed class ExcelAggregateWriter : IExcelAggregateWriter
{
    private readonly List<ExcelDataNode> _aggregatedNodes = [];
    private readonly Dictionary<string, int> _columnMap = new();
    private readonly Dictionary<string, ExcelDataNodePathElement> _pathTypeMap = new();

    private int _currentRow;
    private Export? _currentExport;
    private IExcelWorksheetOperator? _currentSheet;
    private IExcelWorkbookOperator? _currentWorkbook;
    private ExcelDataNode? _dataNode;
    private ExcelDataNode? _aggregatedDataNode;
    private OptionHelper _helper;

    public string Section => "AggregateWriter";

        
    /// <summary>
    /// 确定 _currentExport 已经被初始化的地方使用此属性替换 _currentExport 字段。
    /// </summary>
    private Export CurrentExport => _currentExport!;


    public ExcelAggregateWriter(CollectorOptions options, IReportService reportService)
    {
        ReportService = reportService;
        var validator = new Validator
        {
            ReportService = reportService
        };
        _helper = new OptionHelper(options, validator);
    }

    internal ExcelAggregateWriter(OptionHelper helper, IReportService? reportService)
    {
        ReportService = reportService;
        _helper = helper;
        helper.Validator.ReportService = reportService;
    }


    public ExcelAggregateWriter(IReportService? reportService)
    {
        ReportService = reportService;
        _helper = new OptionHelper(new CollectorOptions(), new Validator());
    }


    public IFilePikerService? FilePikerService { get; set; }
        
    public IReportService? ReportService{ get; set; }

    public void SetOptions(object options)
    {
        switch (options)
        {
            case OptionHelper optionHelper:
                _helper = optionHelper;
                break;

            case CollectorOptions collectorOptions:
                var validator = new Validator
                {
                    ReportService = ReportService
                };
                _helper = new OptionHelper(collectorOptions, validator);
                break;
        }
    }

    public void WriteToExcel(IExcelWorkbookOperator workbook, object data)
    {
        WriteToExcel(workbook, data, CancellationToken.None);
    }

    public void WriteToExcel(IExcelWorkbookOperator workbook, object data, CancellationToken cancellationToken)
    {
        if (data is not ExcelDataNode d)
        {
            ReportService?.Error($"数据类型不符，应为'{nameof(ExcelDataNode)}'。");
            return;
        }

        if (_helper.Options.Exports.Count == 0)
        {
            ReportService?.Error("未添加输出配置，程序已退出。");
            return;
        }
            
        _dataNode = d;

        ReportService?.Log("开始输出工作簿。");

        try
        {
            _currentWorkbook = workbook;

            foreach (var export in _helper.Options.Exports.Values)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    break;
                }

                ReportService?.Log($"开始输出工作表 - '{export.Name}'");
                StartSheet(GetWorksheet(export.Name), export);
                if (Aggregate())
                {
                    WriteSheet(cancellationToken);
                }

                ExitSheet();
                ReportService?.Log($"工作表 - '{export.Name}'输出完成。");
            }
        }
        catch (Exception ex)
        {
            ReportService?.Error(ex.Message);
        }
        finally
        {
            _currentWorkbook?.Save();
            _currentWorkbook?.Close();
            _currentWorkbook = null;
        }

        _dataNode = null;
        _aggregatedNodes.Clear();

        ReportService?.Log("工作簿输出完成。");
    }


    private bool IsRowValid()
    {
        return _currentRow < ExcelHelper.MaxRow;
    }

    private void BuildColumnMap()
    {
        if (_currentExport is null)
        {
            ReportService?.Error("尚未添加输出配置。");
            return;
        }

        if (_currentSheet is null)
        {
            ReportService?.Error($"工作表'{_currentExport.Name}'尚未创建。");
            return;
        }

        var row = _currentExport.StartRow;
        var col = _currentExport.StartColumn;

        _columnMap.Clear();

        foreach (var header in _currentExport.Headers)
        {
            _currentSheet.GetRange(row, col).Value = header;
            _columnMap.SetValue(header, col);
            col++;
        }
    }

    private void StartSheet(IExcelWorksheetOperator worksheet, Export export)
    {
        _currentExport = export;
        _currentSheet = worksheet;
        _currentRow = export.StartRow + 1;
        BuildColumnMap();
    }

    private void ExitSheet()
    {
        _columnMap.Clear();

        if (_currentExport is null || _currentSheet is null)
        {
            return;
        }

        if (_currentExport.Format)
        {
            ReportService?.Log("开始添加默认样式 ...");

            _currentSheet.SetColumnAlignment(ExcelHAlign.Center, ExcelVAlign.Center);
            _currentSheet.AutoFitEntireColumns();
            _currentSheet.ShowGridLines = false;

            var endRow = _currentSheet.EndRow;
            var endCol = _currentSheet.EndColumn;
            var usedRange = _currentSheet.GetRange(_currentExport.StartRow,
                _currentExport.StartColumn, endRow, endCol);
            // borders
            usedRange.SetRangeBorderStyle(BorderPosition.Around | BorderPosition.Horizontal | BorderPosition.Vertical,
                BorderStyle.Dashed);

            var headerRange = _currentSheet.GetRange(_currentExport.StartRow,
                _currentExport.StartColumn, _currentExport.StartRow, _currentSheet.EndColumn);
            headerRange.FontStyle = FontType.Bold;
            headerRange.BackgroundColor = ExcelStyleColors.LightGray;

            ReportService?.Log("样式已添加完成。");
        }

        _currentSheet = null;
        _currentExport = null;
    }

    private bool ReorderNodePath(List<ExcelDataNodePathElement> path)
    {
        var index = path.FindIndex(e => e.Type.Equals(CurrentExport.ValueAt));

        if (index <= -1 || !_helper.TryGetType(path[path.Count - 1].Type, out var typeObj))
        {
            return index > -1;
        }

        if (index < path.Count - 1)
        {
            ExcelDataNodePathElement valueAtElement = path[index];
            path.RemoveAt(index);
            path.Add(valueAtElement);
        }
            
        return true;
    }

    private bool Aggregate()
    {
        if (_dataNode is null)
        {
            ReportService?.Error("未添加已读取的数据。");
            return false;
        }

        if (_currentExport is null)
        {
            ReportService?.Error("未添加输出配置。");
            return false;
        }

        if (string.IsNullOrEmpty(_currentExport.ValueAt))
        {
            ReportService?.Error("未配置输出值类型。");
            return false;
        }

        if (!_helper.Options.Types.Settings.ContainsKey(_currentExport.ValueAt))
        {
            ReportService?.Error("输出值类型配置错误：未找到指定类型。");
            return false;
        }

        _aggregatedNodes.Clear();
        _aggregatedDataNode = new ExcelDataNode();

        ReportService?.Log($"开始数据重排，数据类型 - {_currentExport.ValueAt}");

        ExcelDataNode.ForEachChild(_dataNode, args =>
        {
            var node = args.Node;
            if (!node.Type.Equals(_currentExport.ValueAt))
            {
                return;
            }

            foreach (var childNode in node.GetDataChildren())
            {
                var path = childNode.GetPath();
                if (_helper.IsFilteredOut(_currentExport, path) || !ReorderNodePath(path))
                {
                    continue;
                }

                var reordered = _aggregatedDataNode.GetChild(path);
                reordered.Data.CopyValue(childNode.Data);
                _aggregatedNodes.Add(reordered);
            }

            args.IsDfsStopped = true;
        });

        // 排序节点顺序。
        _helper.OrderBy(_currentExport, _aggregatedNodes);

        // 聚合数据节点时，需要在aggregatedNodes中添加重排路径后的数据节点的父节点
        if (_currentExport.Aggregate)
        {
            var grouped = _aggregatedNodes
                .GroupBy(e => e.Parent)
                .Select(e => e.Key)
                .OfType<ExcelDataNode>()
                .ToList();
            _aggregatedNodes.Clear();
            _aggregatedNodes.AddRange(grouped);
        }

        ReportService?.Log($"数据重排已完成，末端节点数 - {_aggregatedNodes.Count}。");
        return true;
    }

    private string MakeIndexValue(int index)
    {
        return _currentExport != null ? $"{_currentExport.IndexName}{index}" : index.ToString();
    }

    private void WriteNodeDataValue(ExcelDataValue val, int column)
    {
        if (val.Data == null)
        {
            return;
        }

        var cell = _currentSheet!.GetRange(_currentRow, column);
        cell.Value = val.Data;
            
        if (val.Color != ExcelStyleColor.Empty)
        {
            cell.FontColor = val.Color;
        }

        cell.NumberFormatLocal = val.NumberFormat;
    }

    private void WriteIndex()
    {
        if (_columnMap.TryGetValue(CurrentExport.IndexName, out var indexCol))
        {
            _currentSheet!.GetRange(_currentRow, indexCol).Value = MakeIndexValue(CurrentExport.Index);
        }
    }

    // 写入数值节点时
    // 1. 如果在ExportIndex.Header中写入成功，结束，否则继续
    // 2. 检查节点类型值，如果包含指定名称的列，则写入数据，否则结束

    private void WriteNodePath(ExcelDataNode node)
    {
        if (_columnMap.TryGetValue(node.Type, out var typeCol))
        {
            _currentSheet!.GetRange(_currentRow, typeCol).Value = node.Name;
        }

        // test
        if (_helper.TryGetType(node.Type, out var typeObj) &&
            !string.IsNullOrEmpty(typeObj.Testing) &&
            _columnMap.TryGetValue(typeObj.Testing, out var testCol) &&
            _helper.TryTest(typeObj.Testing, node.Name, out var markText))
        {
            _currentSheet!.GetRange(_currentRow, testCol).Value = markText;
        }
    }

    // 写入节点值时，需要分两种情况
    // 1. 当前值为值节点，直接写入当前节点的数据
    // 2. 当前值不是值节点，在子节点中查找值节点进行写入

    // 值节点应当是ColumnKey对应的节点

    private void WriteNodeExportIndex(ExcelDataNode node, ExportIndex index)
    {
        // 聚合输出时，数据节点在子节点，列名应当对应子节点名
        if (CurrentExport.Aggregate && !string.IsNullOrEmpty(index.Type))
        {
            if (!index.Type.Equals(node.Type))
            {
                return;
            }

            if (!string.IsNullOrEmpty(index.Value) && !index.Value.Equals(node.Name))
            {
                return;
            }
        }

        if (!_columnMap.TryGetValue(index.Header, out var col) ||
            !node.Data.TryGetValue(index.Index, out var dataValue))
        {
            return;
        }

        WriteNodeDataValue(dataValue, col);
    }

    private void WriteNodeData(ExcelDataNode node)
    {
        if (CurrentExport.Aggregate)
        {
            foreach (var child in node.Children)
            {
                foreach (var index in CurrentExport.Indices)
                {
                    WriteNodeExportIndex(child, index);
                }
            }
        }

        foreach (var index in CurrentExport.Indices)
        {
            WriteNodeExportIndex(node, index);
        }
    }

    private void CalculateGap(string gapName, ExcelDataNode node)
    {
        if (!_helper.TryGetGap(gapName, _pathTypeMap.Values, out var gapObj))
        {
            return;
        }

        // Gap 需要配置 Indices 来指定输出名称
        if (gapObj.Indices.Count == 0)
        {
            return;
        }

        foreach (var index in gapObj.Indices)
        {
            _columnMap.TryGetValue(index.MarkName, out var markCol);
            _columnMap.TryGetValue(index.CalcName, out var calcCol);

            if (markCol <= 0 && calcCol <= 0)
            {
                continue;
            }

            var result = _helper.CalculateGap(gapObj, node.Data, index);

            if (!result.Success)
            {
                continue;
            }

            if (markCol > 0 && !string.IsNullOrEmpty(result.Mark))
            {
                _currentSheet!.GetRange(_currentRow, markCol).Value = result.Mark;
            }

            if (calcCol <= 0)
            {
                continue;
            }

            var cell = _currentSheet!.GetRange(_currentRow, calcCol);
            cell.Value = result.Result;
            cell.NumberFormatLocal = result.NumberFormatLocal;
        }
    }

    private void WriteNode(ExcelDataNode node)
    {
        if (!IsRowValid())
        {
            ReportService?.Error("行数过多，剩余数据已忽略。");
            return;
        }

        _pathTypeMap.Clear();

        WriteIndex();

        var path = node.GetPath();

        foreach (var element in path)
        {
            WriteNodePath(element.Node);
            _pathTypeMap.SetValue(element.Type, element);
        }

        WriteNodeData(node);

        foreach (var gap in CurrentExport.Gaps)
        {
            if (CurrentExport.Aggregate)
            {
                foreach (var childNode in node.Children)
                {
                    _pathTypeMap.SetValue(childNode.Type, new ExcelDataNodePathElement(childNode));
                    CalculateGap(gap, childNode);
                    _pathTypeMap.Remove(childNode.Type);
                }
            }
            else
            {
                CalculateGap(gap, node);
            }
        }

        _currentRow++;
    }

    private void WriteSheet(CancellationToken cancellationToken)
    {
        if (_currentSheet is null)
        {
            return;
        }

        double totalSize = _aggregatedNodes.Count;
        double count = 1;

        foreach (var node in _aggregatedNodes)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return;
            }

            WriteNode(node);
            ReportService?.Progress(count / totalSize * 100, "开始填充数据 ...");
            count++;
        }

        ReportService?.Log("数据填充完成。");
    }

    private IExcelWorksheetOperator GetWorksheet(string sheetName)
    {
        IExcelWorksheetOperator sheet;

        if (_currentWorkbook!.Worksheets.Count == 1 && _currentWorkbook.Worksheets[0].Name == "Sheet1")
        {
            sheet = _currentWorkbook.Worksheets[0];
        }
        else
        {
            sheet = _currentWorkbook.GetWorksheet(sheetName);
        }

        return sheet;
    }
}